﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Nuzzle.Network
{
    public class GameClient : BaseClient
    {
        public Database.Models.AccountModel Account { get; set; }
        public Database.Models.CharacterModel Character { get; set; }

        public short NextDestination = -1;

        public GameClient(SilverSock.SilverSocket socket)
            : base(socket)
        {
            this.Send(new Protocol.Messages.Game.Approach.HelloGameMessage());
        }

        public override void OnClose()
        {
            lock (GameServer.Clients)
            {
                try
                {
                    if (this.Character != null)
                    {
                        if (this.Character.Map != null)
                        {
                            this.Character.Map.Engine.RemovePlayer(this);
                            this.Character.Update();
                        }
                    }
                    Utilities.Logger.Basic("GameClient disconnected !");
                    GameServer.Clients.Remove(this);
                }
                catch (Exception e)
                {
                    Utilities.Logger.Error("Can't disconnect player : " + e.ToString());
                }
            }
        }

        public override void OnPacket(Protocol.NetworkMessageReader message)
        {
            Utilities.Logger.Debug("Received packet : " + message.ID.ToString());
            try
            {
                switch (message.ID)
                {
                    case Protocol.NetworkMessageID.AuthenticationTicketMessage:
                        this.HandleAuthenticationTicketMessage(new Protocol.Messages.Game.Approach.AuthenticationTicketMessage(message));
                        break;

                    case Protocol.NetworkMessageID.CharactersListRequestMessage:
                        this.HandleCharactersListRequestMessage();
                        break;

                    case Protocol.NetworkMessageID.CharacterNameSuggestionRequestMessage:
                        this.HandleCharacterNameSuggestionRequestMessage();
                        break;

                    case Protocol.NetworkMessageID.CharacterCreationRequestMessage:
                        this.HandleCharacterCreationRequestMessage(new Protocol.Messages.Game.Character.Creation.CharacterCreationRequestMessage(message));
                        break;

                    case Protocol.NetworkMessageID.CharacterDeletionRequestMessage:
                        this.HandleCharacterDeletionRequestMessage(new Protocol.Messages.Game.Character.Deletion.CharacterDeletionRequestMessage(message));
                        break;

                    case Protocol.NetworkMessageID.CharacterSelectionMessage:
                        this.HandleCharacterSelectionMessage(new Protocol.Messages.Game.Character.Choice.CharacterSelectionMessage(message));
                        break;

                    case Protocol.NetworkMessageID.ClientKeyMessage:
                        this.HandleClientKeyMessage(new Protocol.Messages.Security.ClientKeyMessage(message));
                        break;

                    case Protocol.NetworkMessageID.MapInformationsRequestMessage:
                        this.HandleMapInformationsRequestMessage();
                        break;

                    case Protocol.NetworkMessageID.GameMapMovementRequestMessage:
                        this.HandleGameMapMovementRequestMessage(new Protocol.Messages.Game.Context.GameMapMovementRequestMessage(message));
                        break;

                    case Protocol.NetworkMessageID.GameMapMovementConfirmMessage:
                        this.HandleGameMapMovementConfirmMessage();
                        break;

                    case Protocol.NetworkMessageID.ChangeMapMessage:
                        this.HandleChangeMapMessage(new Protocol.Messages.Game.Context.Roleplay.ChangeMapMessage(message));
                        break;

                    case Protocol.NetworkMessageID.AdminCommandMessage:
                        this.HandleAdminCommandMessage(new Protocol.Messages.Authorized.AdminCommandMessage(message));
                        break;

                    case Protocol.NetworkMessageID.ChatClientMultiMessage:
                        this.HandleChatClientMultiMessage(new Protocol.Messages.Game.Chat.ChatClientMultiMessage(message));
                        break;
                }
            }
            catch (Exception e)
            {
                Utilities.Logger.Error("Can't handle packet : " + e.ToString());
            }
        }

        public override void Send(Protocol.NetworkMessage message)
        {
            Utilities.Logger.Debug("Send packet : " + message.ProtocolID.ToString());
            base.Send(message);
        }

        #region Send

        public void SendCharactersListMessage()
        {
            var characters = new List<Protocol.Types.Game.Character.Choice.CharacterBaseInformations>();
            foreach (var c in this.Account.Characters)
            {
                characters.Add(c.ToCharacterBaseInformations());
            }
            this.Send(new Protocol.Messages.Game.Character.Choice.CharactersListMessage(false, characters));
            characters.Clear();
        }

        public void SendCharacterCreationResultMessage(Protocol.Enums.CharacterCreationResultEnum result)
        {
            this.Send(new Protocol.Messages.Game.Character.Creation.CharacterCreationResultMessage((byte)result));
        }

        public void SendGameContext(byte context)
        {
            this.Send(new Protocol.Messages.Game.Context.GameContextDestroyMessage());
            this.Send(new Protocol.Messages.Game.Context.GameContextCreateMessage(context));
        }

        public void SendCharacterStatsListMessage()
        {
            this.Send(new Protocol.Messages.Game.Character.Stats.CharacterStatsListMessage(this.Character.ToCharacterCharacteristicsInformations()));
        }

        #endregion

        #region Handlers

        public void HandleAuthenticationTicketMessage(Protocol.Messages.Game.Approach.AuthenticationTicketMessage message)
        {
            var ticket = Managers.TicketManager.GetAndDestroy(message.Ticket);
            if (ticket != null)
            {
                this.Account = ticket.Account;
                Utilities.Logger.Basic("Player '" + this.Account.Username + "' connected on gameserver");
                this.Send(new Protocol.Messages.Game.Approach.AuthenticationTicketAcceptedMessage());
            }
            else
            {
                this.Disconnect();
            }
        }

        public void HandleCharactersListRequestMessage()
        {
            if (this.Account != null)
            {
                this.Send(new Protocol.Messages.Game.Approach.AccountCapabilitiesMessage
                    (this.Account.ID, false, 16383, 16383));
                this.Send(new Protocol.Messages.Game.Approach.ServerOptionalFeaturesMessage(
                    new List<int>() { 1, 2 }));
                this.SendCharactersListMessage();
            }
            else
            {
                this.Disconnect();
            }
        }

        public void HandleCharacterNameSuggestionRequestMessage()
        {
            if (this.Account != null)
            {
                this.Send(new Protocol.Messages.Game.Character.Creation.CharacterNameSuggestionSuccessMessage
                    (Managers.CharacterManager.GetRandomName()));
            }
            else
            {
                this.Disconnect();
            }
        }

        public void HandleCharacterCreationRequestMessage(Protocol.Messages.Game.Character.Creation.CharacterCreationRequestMessage message)
        {
            if (this.Account != null)
            {
                if (!Managers.CharacterManager.ExistName(message.Name))
                {
                    if (this.Account.Characters.Count < 5)
                    {
                        var character = new Database.Models.CharacterModel();
                        character.ID = Database.DatabaseCache.CHARACTER_NEXT_ID;
                        Database.DatabaseCache.CHARACTER_NEXT_ID++;
                        character.Nickname = message.Name;
                        character.Owner = this.Account.ID;
                        character.Breed = message.Breed;
                        character.Gender = message.Sex ? 1 : 0;

                        #region Colors

                        //Set default color
                        for (int i = 0; i <= message.Colors.Count - 1; i++)
                        {
                            if (message.Colors[i] == -1)
                            {
                                if (character.Gender == 0)
                                {
                                    message.Colors[i] = character.BreedData.MaleColors[i] | (1 + i) * 0x1000000;
                                }
                                else
                                {
                                    message.Colors[i] = character.BreedData.FemaleColors[i] | (1 + i) * 0x1000000; ;
                                }
                            }
                            else
                            {
                                if (character.Gender == 0)
                                {
                                    message.Colors[i] = message.Colors[i] | (1 + i) * 0x1000000;
                                }
                                else
                                {
                                    message.Colors[i] = message.Colors[i] | (1 + i) * 0x1000000; ;
                                }
                            }
                        }
                        character.Colors = string.Join(",", message.Colors);

                        #endregion

                        character.Skin = int.Parse(character.Breed.ToString() + character.Gender.ToString());
                        character.Scal = Utilities.Settings.AppSettings.GetIntElement("Gameplay.StartScal");
                        character.Level = Utilities.Settings.AppSettings.GetIntElement("Gameplay.StartLevel");
                        if (Utilities.Settings.AppSettings.GetIntElement("Gameplay.StartMap") != -1)
                        {
                            character.MapID = Utilities.Settings.AppSettings.GetIntElement("Gameplay.StartMap");
                            character.CellID = Utilities.Settings.AppSettings.GetIntElement("Gameplay.StartCell");
                            character.Direction = Utilities.Settings.AppSettings.GetIntElement("Gameplay.StartDir");
                        }
                        else
                        {
                            character.MapID = character.BreedData.StartMap;
                            character.CellID = character.BreedData.StartCell;
                            character.Direction = 3;
                        }

                        this.Account.Characters.Add(character);
                        Database.DatabaseCache.CharactersCache.Add(character);
                        character.Insert();

                        this.SendCharacterCreationResultMessage(Protocol.Enums.CharacterCreationResultEnum.OK);
                        this.SendCharactersListMessage();
                    }
                    else
                    {
                        this.SendCharacterCreationResultMessage(Protocol.Enums.CharacterCreationResultEnum.ERR_TOO_MANY_CHARACTERS);
                    }
                }
                else
                {
                    this.SendCharacterCreationResultMessage(Protocol.Enums.CharacterCreationResultEnum.ERR_NAME_ALREADY_EXISTS);
                }
            }
        }

        public void HandleCharacterDeletionRequestMessage(Protocol.Messages.Game.Character.Deletion.CharacterDeletionRequestMessage message)
        {
            var character = this.Account.GetCharacter(message.CharacterID);
            if (character != null)
            {
                this.Account.Characters.Remove(character);
                Database.DatabaseCache.CharactersCache.Remove(character);
                character.Delete();

                this.SendCharactersListMessage();

                //TODO: Delete items
            }
            else
            {
                this.Disconnect();
            }
        }

        public void HandleCharacterSelectionMessage(Protocol.Messages.Game.Character.Choice.CharacterSelectionMessage message)
        {
            var character = this.Account.GetCharacter(message.ID);

            if (character != null)
            {
                Utilities.Logger.Basic("Player '" + this.Account.Username + "' select the character '" + character.Nickname + "'");
                this.Character = character;
                this.Send(new Protocol.Messages.Game.Character.Choice.CharacterSelectedSuccessMessage(character.ToCharacterBaseInformations()));
            }
            else
            {
                this.Disconnect();
            }
        }

        public void HandleClientKeyMessage(Protocol.Messages.Security.ClientKeyMessage message)
        {
            if (this.Character != null)
            {
                this.SendGameContext(1);
                this.SystemMessage(Utilities.Basic.ReplaceBySettingsVar(Utilities.Settings.AppSettings.GetStringElement("General.MOTD")),
                    Utilities.Settings.AppSettings.GetStringElement("Colors.MOTD"));
                this.Teleport(this.Character.MapID, this.Character.CellID);

                this.SendCharacterStatsListMessage();
            }
        }

        public void HandleMapInformationsRequestMessage()
        {
            if (this.Character.Map != null)
            {
                this.Character.Map.Engine.SendComplementaryInformations(this);
            }
        }

        public void HandleGameMapMovementRequestMessage(Protocol.Messages.Game.Context.GameMapMovementRequestMessage message)
        {
            if (this.Character.Map != null)
            {
                var pathfinding = new Managers.MapManager.Pathfinding(message.KeyMovements);
                if (!pathfinding.IsEmpty)
                {
                    var destination = pathfinding.Destination;
                    this.NextDestination = destination;
                    Utilities.Logger.Debug("Move on cell : " + this.NextDestination);
                    this.Character.Map.Engine.Send(new Protocol.Messages.Game.Context.GameMapMovementMessage
                        (pathfinding.Movements, this.Character.ID));
                }
                else
                {
                    //TODO: What the fuck ?
                }
            }
        }

        public void HandleGameMapMovementConfirmMessage()
        {
            if (this.NextDestination != -1)
            {
                this.Character.CellID = this.NextDestination;
                this.NextDestination = -1;
            }
        }

        public void HandleChangeMapMessage(Protocol.Messages.Game.Context.Roleplay.ChangeMapMessage message)
        {
            if (this.Character.Map != null)
            {
                if (Managers.MapManager.GetMap(message.MapID) != null)
                {
                    this.Character.Map.Engine.SwitchMap(this, message.MapID);
                }
            }
        }

        public void HandleAdminCommandMessage(Protocol.Messages.Authorized.AdminCommandMessage message)
        {
            if (this.Character != null)
            {
                Managers.AdminManager.ClientCommand(this, message.Content);
            }
        }

        public void HandleChatClientMultiMessage(Protocol.Messages.Game.Chat.ChatClientMultiMessage message)
        {
            if (this.Character != null)
            {
                Managers.ChatManager.ChatMessage(this, message.Content, message.Channel);
            }
        }

        #endregion

        #region Methods

        public void Teleport(int mapid, int cellid)
        {
            if (this.Character.Map != null)
            {
                this.Character.Map.Engine.RemovePlayer(this);
            }

            this.Character.MapID = mapid;
            this.Character.CellID = cellid;

            if (this.Character.Map != null)
            {
                this.Character.Map.Engine.AddPlayer(this);
            }
        }

        public void ConsoleMessage(string message, Protocol.Enums.ConsoleMessageTypeEnum type)
        {
            this.Send(new Protocol.Messages.Authorized.ConsoleMessage(message, (byte)type));
        }

        public void SystemMessage(string message, string color = "")
        {
            if (color != "")
            {
                message = "<font color=\"" + color + "\">" + message + "</font>";
            }
            this.Send(new Protocol.Messages.Game.Basic.TextInformationMessage(0, 0, new string[1] { message }));
        }

        #endregion
    }
}
